package org.opens.netty.decoder;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.opens.netty.util.ByteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

@Slf4j
public class CustomDelimiterDecoder extends ByteToMessageDecoder {

    private static final Logger logger = LoggerFactory.getLogger(CustomDelimiterDecoder.class);

    // 分隔符对应的字符串
    private final String delimiter;
    // 分隔符对应的byte数组
    private final byte[] delimiterArray;
    // 允许的帧长度
    private final int allowFrameLength;

    //private final List<Integer> allowFrameLengthList;

    //private final String allowFrameLengthStr;


    public CustomDelimiterDecoder(String delimiter, int allowFrameLength) {
        this.delimiter = delimiter;
        this.delimiterArray = ByteUtil.hexToByteArray(delimiter);
        this.allowFrameLength = allowFrameLength;
        //this.allowFrameLengthList = Stream.of(allowFrameLengths.split(",")).map(Integer::parseInt).sorted().collect(Collectors.toList());
        //this.allowFrameLengthStr = CollectionUtil.join(this.allowFrameLengthList, ",");
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        log.info("可读取字节数为：{}, 对应串为：{}", in.readableBytes(), ByteBufUtil.hexDump(in));

        // 短数据缓存-判断头部决定是否继续缓存
        // 如果缓冲区数据长度还没有超过帧头就直接返回
        if (in.readableBytes() < delimiterArray.length) {
            log.info("数据长度不够，存入缓冲区");
            return;
        } else if (in.readableBytes() == delimiterArray.length) {
            if (!startWith(in, in.readerIndex(), in.writerIndex(), delimiterArray)) {
                // 移动读指针到末尾
                in.readerIndex(in.writerIndex());
                // 将0~读指针-整个缓冲区数据清空
                in.discardReadBytes();
                log.info("缓冲区头部不匹配，清空缓冲区数据");
                return;
            }
        }

        // 短型数据缓存-或丢弃
        if (in.readableBytes() < this.allowFrameLength) {
            int headerIndex = indexOf(in, in.readerIndex(), in.writerIndex(), delimiterArray);
            if (headerIndex == -1) {
                // 移动读指针到末尾
                in.readerIndex(in.writerIndex());
                // 将0~读指针-整个缓冲区数据清空
                in.discardReadBytes();
                log.info("缓冲区不匹配且整个数据不包含帧头，清空缓冲区数据");
                return;
            } else if (headerIndex > 0) {
                // 将读指针移动到headerIndex处
                in.skipBytes(headerIndex);
                // 将0~headerIndex-缓冲区数据清空（也就是杂数据清空）
                in.discardReadBytes();
                log.info("缓冲区包含帧头，但帧头前包含杂数据，跳过杂数据");
                return;
            }

            // 等于0表示帧头匹配
            log.info("数据长度不够，存入缓冲区");
            return;
        }

        // 中长型数据-是否包含头部
        int headerIndex = indexOf(in, in.readerIndex(), in.writerIndex(), delimiterArray);
        if (headerIndex == -1) {
            // 移动读指针到末尾
            in.readerIndex(in.writerIndex());
            // 将0~读指针-整个缓冲区数据清空
            in.discardReadBytes();
            log.info("缓冲区不匹配且整个数据不包含帧头，清空缓冲区数据");
            return;
        }

        // 中长数据-包含头部-但是头部前有杂数据-跳过杂数据
        if (!startWith(in, in.readerIndex(), in.writerIndex(), delimiterArray)) {
            // 移动读指针到headerIndex
            in.skipBytes(headerIndex);
            // 将0~headerIndex-整个缓冲区数据清空
            in.discardReadBytes();
            log.info("缓冲区包含帧头，但帧头前包含杂数据，跳过杂数据");
        }

        // 与允许长度等长数据
        if (in.readableBytes() == this.allowFrameLength) {
            // 此处表示没粘包，并且数据长度累计够了则直接送达下一个处理器
            ByteBuf frame = extractFrame(in, in.readerIndex(), in.writerIndex());
            out.add(frame);
            // 移动读指针到末尾
            in.readerIndex(in.writerIndex());
            // 将0~读指针-整个缓冲区数据清空
            in.discardReadBytes();
            return;
        }

        // 长型数据-包含帧头-以帧头开始
        int startIndex = -1;
        while ((startIndex = indexOf(in, in.readerIndex(), in.writerIndex(), delimiterArray)) >= 0) {
            int nextIndex = indexOf(in, startIndex + delimiterArray.length, in.writerIndex(), delimiterArray);
            // 如果没有查找到了一个帧头
            if (nextIndex >= 0) {
                int frameLength = nextIndex - startIndex;
                if (this.allowFrameLength == frameLength) {
                    ByteBuf frame = extractFrame(in, startIndex, frameLength);
                    out.add(frame);
                } else {
                    // 这里表示两个帧头间的内容不符合指定的长度，是错误的帧数据
                    logger.warn("通过帧头获取帧长 {} 与设定帧长 {} 不符合, 当前数据丢弃", frameLength, this.allowFrameLength);
                }
                // in.readerIndex(nextIndex); 的作用，是将 readerIndex 移动到 nextIndex 位置，从而使得下一次读取操作从这个位置开始。
                in.readerIndex(nextIndex);  // Update reader index to next delimiter
            } else {
                int _readableBytes = in.readableBytes();
                if (_readableBytes > this.allowFrameLength) {
                    log.info("长数据最后一段数据长度超过允许长度，抛弃数据！");
                    // 将0~writerIndex-缓冲区数据清空
                    in.readerIndex(in.writerIndex());
                    in.discardReadBytes();
                    return;
                } else if (_readableBytes < this.allowFrameLength) {
                    log.info("长数据最后一段数据长度不够，存入缓冲区");
                    // 将0~readerIndex-缓冲区数据清空, 剩余的数据加入下一次
                    in.discardReadBytes();
                    return;
                } else {
                    ByteBuf frame = extractFrame(in, startIndex, _readableBytes);
                    out.add(frame);
                    // 将0~writerIndex-缓冲区数据清空
                    in.readerIndex(in.writerIndex());
                    in.discardReadBytes();
                    return;
                }
            }
        }

    }

    private int indexOf(ByteBuf haystack, int start, int end, byte[] needle) {
        for (int i = start; i <= end - needle.length; i++) {
            int j = 0;
            while (j < needle.length && haystack.getByte(i + j) == needle[j]) {
                j++;
            }
            if (j == needle.length) {
                return i;
            }
        }
        return -1;
    }

    private boolean equals(ByteBuf haystack, int start, int end, byte[] needle) {
        if (end - start != needle.length) {
            return false;
        }

        for (int i = start; i < end; i++) {
            byte tempByte = haystack.getByte(i);
            byte needleByte = needle[i - start];
            if (tempByte != needleByte) {
                return false;
            }
        }

        return true;
    }

    private boolean startWith(ByteBuf haystack, int start, int end, byte[] needle) {
        for (int i = start; i < end; i++) {
            byte tempByte = haystack.getByte(i);
            int j = i - start;
            if (j >= needle.length) {
                break;
            }
            byte needleByte = needle[j];
            if (tempByte != needleByte) {
                return false;
            }
        }
        return true;
    }

    private ByteBuf extractFrame(ByteBuf buffer, int index, int length) {
        ByteBuf frame = buffer.slice(index, length);
        // 在 Netty 中，ByteBuf 是引用计数对象。每个 ByteBuf 实例都有一个引用计数器，当计数器降到零时，该 ByteBuf 会被释放，内存会被回收。retain() 方法的作用是增加引用计数，以确保 ByteBuf 不会在你还需要使用它的时候被释放。
        frame.retain();
        return frame;
    }
}



